﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sora.HelpSystem.Package;
using Sora.HelpSystem.Package.Pages;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Input;

namespace Sora.HelpSystem
{
    public class HelpDocumentProvider
    {
        public HelpDocument Document { get; private set; }

        public SearchProvider SearchProvider { get; private set; }

        public HelpDocumentProvider(HelpDocument document)
        {
            if (document == null)
                throw new ArgumentNullException("document");

            Document = document;
            SearchProvider = new SearchProvider(this);
        }

        public bool IsHelpUri(Uri uri)
        {
            if (uri == null)
                throw new ArgumentNullException("uri");
            else
                return uri.AbsoluteUri.StartsWith(HelpDocument.HelpDocumentUriContentPrefix, StringComparison.OrdinalIgnoreCase);
        }

        public Uri StartPageUri
        {
            get { return Document.StartPageUri; }
        }

        public HelpPage ResolveUri(Uri uri, bool canCreate = false, bool canGenerateErrorPage = true)
        {
            if (uri == null)
                throw new ArgumentNullException("uri");
            else if (!IsHelpUri(uri))
                throw new NotSupportedException(uri.AbsoluteUri);
            else
            {
                var internalUri = uri.AbsoluteUri.Substring(HelpDocument.HelpDocumentUriContentPrefix.Length);

                try
                {
                    if (internalUri.Contains("search?"))
                    {
                        /* we check if it's a search request */
                        string searchBasePage = uri.AbsoluteUri.Substring(0, uri.AbsoluteUri.IndexOf("search?")) + "search";

                        var testPage = ResolveUri(new Uri(searchBasePage), false, true);
                        var searchPage = ResolveUri(new Uri(Document.DocumentBaseUri + "/search"), false, false);

                        if (testPage == searchPage)
                        {
                            /* it's a search request */
                            var query = internalUri.Substring(internalUri.IndexOf("search?") + "search?".Length);
                            var result = SearchProvider.Search(query);

                            searchPage.FlowContent.DiscardChanges();

                            foreach (var searchResult in result)
                            {
                                AppendSearchResult(searchPage.FlowContent.Source, searchResult);
                            }

                            return searchPage;
                        }
                    }

                    if (canGenerateErrorPage && !canCreate && !Document.HelpContent.Root.ChildExists(internalUri))
                    {
                        /* Try to locate the document error page */
                        var notFoundPagePageUri = new Uri(Document.DocumentBaseUri.AbsoluteUri + "/error404");

                        if (notFoundPagePageUri == uri)
                        {
                            /* Error locating the error page ... */
                            throw new PageLoadException(String.Format("Error locating the error page: {0}", notFoundPagePageUri));
                        }
                        else
                        {
                            var res = ResolveUri(notFoundPagePageUri);

                            var errorParagraph = new Paragraph();
                            errorParagraph.Inlines.Add(new Run(uri.ToString()));

                            res.FlowContent.DiscardChanges();
                            res.FlowContent.Source.Blocks.Add(errorParagraph);

                            return res;
                        }
                    }

                    return Document.HelpContent.Root.Child(internalUri, canCreate);
                }
                catch (Exception ex)
                {
                    if (canGenerateErrorPage)
                    {
                        /* Try to locate the document error page */
                        var errorPageUri = new Uri(Document.DocumentBaseUri.AbsoluteUri + "/errorException");

                        if (errorPageUri == uri)
                        {
                            /* Error locating the error page ... */
                            throw new PageLoadException(String.Format("Error locating the error page: {0}", errorPageUri));
                        }
                        else
                        {
                            var res = ResolveUri(errorPageUri);
                            var errorParagraph = new Paragraph();
                            errorParagraph.Inlines.Add(new Run(ex.ToString()));

                            res.FlowContent.DiscardChanges();
                            res.FlowContent.Source.Blocks.Add(errorParagraph);

                            return res;
                        }
                    }
                    else
                        throw new PageLoadException(ex.Message, ex);
                }
            }                
        }

        private void AppendSearchResult(FlowDocument flowDocument, HelpPage searchResult)
        {
            Paragraph p = new Paragraph();

            Hyperlink link = new Hyperlink();
            link.Cursor = Cursors.Hand;
            link.Inlines.Add(new Bold(new Run(searchResult.SafePageTitle)) { Foreground = Brushes.CornflowerBlue, FontSize = 14 });
            link.NavigateUri = searchResult.Uri;

            p.Inlines.Add(new Run("\n"));
            p.Inlines.Add(link);

            flowDocument.Blocks.Add(p);

            Paragraph excerp = new Paragraph();
            excerp.Padding = new System.Windows.Thickness(20, 0, 0, 10);
            excerp.Inlines.Add(new Run(GetExcerp(searchResult)));

            flowDocument.Blocks.Add(excerp);
        }

        private string GetExcerp(HelpPage searchResult)
        {
            var doc = searchResult.FlowContent.Source;

            if (doc == null || doc.Blocks.Count < 1)
                return String.Empty;
            else
            {
                foreach (var block in doc.Blocks)
                {
                    var para = block as Paragraph;
                    if (para != null)
                    {
                        TextRange range = new TextRange(para.ContentStart, para.ContentEnd);

                        var txt = range.Text.Trim();

                        if (!String.IsNullOrEmpty(txt))
                        {
                            return txt.Length > 2000 ? txt.Substring(0, 2000) : txt;
                        }
                    }
                }

                return String.Empty;
            }
        }
    }
}
